package server;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;

import communication.LoginResponseError;
import communication.Message;
import communication.MessageType;
import communication.server.ClientHandle;
import communication.server.GMHandle;
import communication.server.Handle;
import exception.BadArgumentValueException;
import exception.ParseException;

/***
 * @author Dymitr Załunin
 */
public class Main {

	private static GamePool gamePool = null;

	private static GMHandle gmfoot = null;
	
	private static HashMap<String, ClientServiceHandle> clientThreads = new HashMap<String, ClientServiceHandle>();
	
	private static boolean inChampionshipMode;
	private static int playersNumberToStartChampionship;
	private static String championshipGameId;
	private static boolean championshipInProgress = false;
	
	private static final String ChampionshipArgString = "--championship"; 
	private static final String PortArgString = "--port";

	private static void usage() {
		System.err
				.println("usage: ./player " + ChampionshipArgString + "\"YY\" XX " + PortArgString + " ZZZZ");
		System.err.println("\"YY\" - game id");
		System.err
				.println("XX - the number of players who must connect before championship may start");
		System.err.println("ZZZZ -- the port number to listen on");
		System.exit(-1);
	}

	/**
	 * 
	 * @param handle
	 * @return GMHandle or ClientHandle.
	 */
	private static Runnable parseLoginQuerry(Handle handle, Socket socket) {
		Message msg;
		Runnable hand = null;
		try {
			msg = handle.receiveMessage();
			if (msg.getType() == MessageType.GAME_MASTER_LOGIN) {
				GMHandle gm = new GMHandle(handle.getHndConnection()
						.getInStream(), handle.getHndConnection()
						.getOutStream());
				if (gamePool != null) {
					try {
						gm.sendLoginResponse(false, LoginResponseError.MASTER_FOR_THIS_GAME_ALREADY_REGISTERED);
					} catch (IOException e) {
						e.printStackTrace();
					}
					return null;
				}
				if(inChampionshipMode && !msg.getGameMasterLogin().getGameType().equals(championshipGameId)) {
					try {
						gm.sendLoginResponse(false, LoginResponseError.WRONG_GAME_TYPE_DESCRIPTION_DATA);
					} catch (IOException e) {
						e.printStackTrace();
					}
					return null;
				}
				gamePool = new GamePool(gm, msg.getGameMasterLogin()
						.getPlayersMin());
				hand = new GMService(gamePool, socket);
				gmfoot = gm;

				try {
					gm.sendLoginResponse(true);
				} catch (BadArgumentValueException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}

			} else if (msg.getType() == MessageType.PLAYER_LOGIN) {					
				ClientHandle cl = new ClientHandle(handle.getHndConnection()
						.getInStream(), handle.getHndConnection()
						.getOutStream());
				cl.setNick(msg.getPlayerLogin().getNick());
				hand = new ClientService(cl, gmfoot, socket);
				if(championshipInProgress) {
					try {
						cl.sendLoginResponse(false, LoginResponseError.PLAYERS_POOL_OVERFLOW);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if (gamePool == null) {
					try {
						cl.sendLoginResponse(false, LoginResponseError.IMPROPER_GAME_TYPE);
					} catch (IOException e) {
						e.printStackTrace();
					}
				} else {
					try {
						gamePool.addClient(cl);
						if(inChampionshipMode) {
							if(gamePool.getNumberOfWaitingPlayers() == playersNumberToStartChampionship) {
								Championship championship = new Championship(gamePool, clientThreads);
								Thread thread = new Thread(championship);
								championshipInProgress = true;
								thread.start();
							}
						} else {
							if(gamePool.getNumberOfWaitingPlayers() == gamePool.getMinNumberOfPlayersToStartGame())
								gamePool.startPlay();
						}
						try {
							cl.sendLoginResponse(true);
						} catch (BadArgumentValueException e) {
							e.printStackTrace();
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

			}
		} catch (ParseException ignore) {
		} catch (SocketException e) {
			e.printStackTrace();
		}
		return hand;
	}

	public static void main(String[] args) {
		try {
			int port = 0;
			if (args.length != 2 && args.length != 5) {
				usage();
				
			}
			if (args.length == 2) {
				if(!args[0].equals(PortArgString)) {
					usage();
					System.exit(-1);
				}
				inChampionshipMode = false;
				port = Integer.parseInt(args[1]);
			}
			if (args.length == 5) {
				if(!args[0].equals(ChampionshipArgString) || !args[3].equals(PortArgString)) {
					usage();
					System.exit(-1);
				}
				inChampionshipMode = true;
				playersNumberToStartChampionship = Integer.parseInt(args[2]);
				championshipGameId = args[1];
				port = Integer.parseInt(args[4]);
			}
			for (int i = 0; i < args.length; ++i)
				System.err.println(args[i]);
 			System.err.println("Server started...");

			ServerSocket sock = new ServerSocket(port);

			while (true) {

				Socket s = sock.accept();

				Runnable client = parseLoginQuerry(new Handle(
						new DataInputStream(s.getInputStream()),
						new DataOutputStream(s.getOutputStream())), s);
				if (client == null)
					continue;
				ClientService clientService = null;
				GMService gmService = null;
				Thread thread;

				if (client instanceof ClientService) {
					clientService = (ClientService) client;
					thread = new Thread(clientService);
					clientThreads.put(clientService.getNick(), new ClientServiceHandle(clientService, thread));
					thread.start();
				} else if (client instanceof GMService) {
					gmService = (GMService) client;
					thread = new Thread(gmService);
					thread.start();
				}

			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}